#include <ClanLib/core.h>
#include <ClanLib/application.h>
#include <ClanLib/display.h>
#ifdef USE_SDL
	#include <ClanLib/sdl.h>
#else
	#include <ClanLib/gl.h>
#endif
#include "level.h"

void Level::draw(Viewport &viewport)
{
	CL_Rect world_area = viewport.get_world_area();
	int start_tile_x = world_area.left / tileset.get_tile_width(); if(start_tile_x < 0) start_tile_x = 0;
	int start_tile_y = world_area.top / tileset.get_tile_height(); if(start_tile_y < 0) start_tile_y = 0;
	int end_tile_x = world_area.right / tileset.get_tile_width(); if(end_tile_x > (width - 1)) end_tile_x = width - 1;
	int end_tile_y = world_area.bottom / tileset.get_tile_height(); if(end_tile_y > (height - 1)) end_tile_y = height - 1;

	int x, y, tile;
	CL_Point pos;
	for(x = start_tile_x; x <= end_tile_x; x++){
		for(y = start_tile_y; y <= end_tile_y; y++){
			tile = x + (y * width);
			pos.x = x * tileset.get_tile_width();
			pos.y = y * tileset.get_tile_height();
			if(get_tile_solid(tile))
				tileset.draw(levelData[tile], pos, viewport);
		}
	}
}

char*& Level::get_level_data()
{
	return levelData;
}

int Level::get_tile_id(CL_Pointf &pos)
{
	int id = 0;
	id = (int)(pos.x / tileset.get_tile_width());
	id += (int)(pos.y / tileset.get_tile_height()) * width;
	return id;
}

int Level::get_tile_id(int &x, int &y)
{
	int id = 0;
	id = (int)(x / tileset.get_tile_width());
	id += (int)(y / tileset.get_tile_height()) * width;
	return id;
}
int Level::get_tile_above(int &tile)
{
	return (tile - width);
}

int Level::get_tile_below(int &tile)
{
	return (tile + width);
}

int Level::get_tile_leftof(int &tile)
{
	return (tile - 1);
}

int Level::get_tile_rightof(int &tile)
{
	return (tile + 1);
}

CL_Rect Level::get_tile_area(int &tile)
{
	CL_Point pos;
	pos.x = (tile % width) * tileset.get_tile_width();
	pos.y = (int)(tile / width) * tileset.get_tile_height();
	return CL_Rect(pos, CL_Size(tileset.get_tile_width(), tileset.get_tile_height()));
}

bool Level::get_tile_solid(int &tile)
{
	return (levelData[tile] != -1);
}

char Level::get_tile_adjacent(int &tile)
{
	char map = 0;
	if(levelData[tile - (width + 1)] != -1)
		map += 1;
	if(levelData[tile - width] != -1)
		map += 2;
	if(levelData[tile - (width - 1)] != -1)
		map += 4;
	if(levelData[tile - 1] != -1)
		map += 8;
	if(levelData[tile + 1] != -1)
		map += 16;
	if(levelData[tile + (width - 1)] != -1)
		map += 32;
	if(levelData[tile + width] != -1)
		map += 64;
	if(levelData[tile + (width + 1)] != -1)
		map += 128;
	return map;
}

int Level::get_smallest_tiledim()
{
	if(tileset.get_tile_width() < tileset.get_tile_height())
		return tileset.get_tile_width();
	else
		return tileset.get_tile_height();
}

int Level::get_largest_tiledim()
{
	if(tileset.get_tile_width() < tileset.get_tile_height())
		return tileset.get_tile_height();
	else
		return tileset.get_tile_width();
}

Level::Level()
{
}

Level::Level(const std::string &resource_id, CL_ResourceManager *manager)
{
		// Load resource
		CL_Resource resource = manager->get_resource(resource_id);
		resource.load();

		// Get level data from resource
		LevelResourceData *data = 
			(LevelResourceData *) resource.get_data("level_data");

		// Throw error if data of wrong type
		if(!data)
			throw CL_Error("Resource '" + resource_id + "' is not of type 'level'");

		// Set variables
		width = data->width;
		//std::cout << "Width: " << width << std::endl;
		height = data->height;
		//std::cout << "Height: " << height << std::endl;
		levelData = data->levelData;

		//Load tileset
		//resource_id + "/
		tileset = Tileset("tileset", manager);

		// Unload resource
		resource.unload();
}

Level::~Level()
{
}

LevelResourceData::LevelResourceData(CL_Resource &resource) : CL_ResourceData(resource)
{
	levelData = NULL;
}

LevelResourceData::~LevelResourceData()
{
	if(levelData){
		free((void*) levelData);
	}
}

void LevelResourceData::on_load()
{
	CL_DomElement element = get_resource().get_element();
	width = CL_String::to_int(element.get_attribute("width"));
	height = CL_String::to_int(element.get_attribute("height"));

	std::string handle = element.get_attribute("file");
	CL_InputSource* levelFile = get_resource().get_manager().get_resource_provider()->open_source(handle);
	if(levelFile->size() != width*height)
		throw CL_Error("File '" + handle + "' is not the correct size for given dimensions");

	levelData = (char*) malloc(levelFile->size());
	if(!levelData)
		throw CL_Error("Memory allocaton failure whilst loading level data");

	levelFile->read(levelData, levelFile->size());
	levelFile->close();
}
